package academic.ui.client.widgets;

import com.google.gwt.animation.client.Animation;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.HasClickHandlers;
import com.google.gwt.event.logical.shared.CloseEvent;
import com.google.gwt.event.logical.shared.CloseHandler;
import com.google.gwt.event.logical.shared.HasCloseHandlers;
import com.google.gwt.event.logical.shared.HasOpenHandlers;
import com.google.gwt.event.logical.shared.OpenEvent;
import com.google.gwt.event.logical.shared.OpenHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.HasText;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.Widget;

import ui.client.GenericContainerTag;

public class SliderPanel extends Composite implements 
    HasOpenHandlers<SliderPanel>, HasCloseHandlers<SliderPanel> {
  

  /**
   * Used to wrap widgets in the header to provide click support. Effectively
   * wraps the widget in an <code>anchor</code> to get automatic keyboard
   * access.
   */
  private final class ClickableHeader extends SimplePanel {

    private ClickableHeader() {
      // Anchor is used to allow keyboard access.
      super(DOM.createAnchor());
      Element elem = getElement();
      DOM.setElementProperty(elem, "href", "javascript:void(0);");
      // Avoids layout problems from having blocks in inlines.
      DOM.setStyleAttribute(elem, "display", "block");
     
      setStyleName("header");
    }

    @Override
    public void onBrowserEvent(Event event) {
      // no need to call super.
      switch (DOM.eventGetType(event)) {
        case Event.ONCLICK:
          // Prevent link default action.
          DOM.eventPreventDefault(event);
         //setOpen(!isOpen);
      }
    }


  }

  /**
   * An {@link Animation} used to open the content.
   */
  private static class ContentAnimation extends Animation {
    /**
     * Whether the item is being opened or closed.
     */
    private boolean isOpening;

    private SliderPanel curPanel;

    /**
     * Open or close the content.
     * 
     * @param panel the panel to open or close
     * @param animate true to animate, false to open instantly
     */
    public void setOpen(SliderPanel panel, boolean animate) {
      // Immediately complete previous open
      cancel();

      // Open the new item
      if (animate) {
        curPanel = panel;
        isOpening = panel.isOpen;
        run(ANIMATION_DURATION);
      } else {
        panel.contentWrapper.setVisible(panel.isOpen);
        if (panel.isOpen) {
          // Special treatment on the visible case to ensure LazyPanel works
          panel.getContent().setVisible(true);
        }
      }
    }

    @Override
    protected void onComplete() {
      if (!isOpening) {
        curPanel.contentWrapper.setVisible(false);
      }
      DOM.setStyleAttribute(curPanel.contentWrapper.getElement(), "height",
          "auto");
      curPanel = null;
    }

    @Override
    protected void onStart() {
      super.onStart();
      if (isOpening) {
        curPanel.contentWrapper.setVisible(true);
        // Special treatment on the visible case to ensure LazyPanel works
        curPanel.getContent().setVisible(true);
      }
    }

    @Override
    protected void onUpdate(double progress) {
      int scrollHeight = DOM.getElementPropertyInt(
          curPanel.contentWrapper.getElement(), "scrollHeight");
      int height = (int) (progress * scrollHeight);
      if (!isOpening) {
        height = scrollHeight - height;
      }
      height = Math.max(height, 1);
      DOM.setStyleAttribute(curPanel.contentWrapper.getElement(), "height",
          height + "px");
      DOM.setStyleAttribute(curPanel.contentWrapper.getElement(), "width",
          "auto");
    }
  }

  /**
   * The duration of the animation.
   */
  private static final int ANIMATION_DURATION = 900;

  // Stylename constants.


  private static final String STYLENAME_SUFFIX_CLOSED = "closed";


  private static final String STYLENAME_CONTENT = "content";

  /**
   * The {@link Animation} used to open and close the content.
   */
  private static ContentAnimation contentAnimation;

  /**
   * top level widget. The first child will be a reference to {@link #header}.
   * The second child will be a reference to {@link #contentWrapper}.
   */
  private final GenericContainerTag mainPanel = new GenericContainerTag("div");

  /**
   * The wrapper around the content widget.
   */
  private final SimplePanel contentWrapper = new SimplePanel();

  /**
   * holds the header widget.
   */
  private final ClickableHeader header = new ClickableHeader();

  private boolean isAnimationEnabled = false;

  private boolean isOpen = false;

  /**
   * Creates an empty DisclosurePanel that is initially closed.
   */
  public SliderPanel() {
    initWidget(mainPanel);
    mainPanel.add(header);
    mainPanel.add(contentWrapper);
    DOM.setStyleAttribute(contentWrapper.getElement(), "padding", "0px");
    DOM.setStyleAttribute(contentWrapper.getElement(), "overflow", "hidden");
    setAnimationEnabled(true);
    setContentDisplay(false);
  }


  public void add(Widget w) {
    if (this.getContent() == null) {
      setContent(w);
    } else {
      throw new IllegalStateException(
          "A SliderPanel can only contain two Widgets.");
    }
  }

  public HandlerRegistration addCloseHandler(
      CloseHandler<SliderPanel> handler) {
    return addHandler(handler, CloseEvent.getType());
  }

  /**
   * Attaches an event handler to the panel to receive {@link DisclosureEvent}
   * notification.
   * 
   * @param handler the handler to be added (should not be null)
   * @deprecated Use {@link DisclosurePanel#addOpenHandler(OpenHandler)} and
   *             {@link DisclosurePanel#addCloseHandler(CloseHandler)} instead
   */

  public HandlerRegistration addOpenHandler(OpenHandler<SliderPanel> handler) {
    return addHandler(handler, OpenEvent.getType());
  }

  public void clear() {
    setContent(null);
  }

  /**
   * Gets the widget that was previously set in {@link #setContent(Widget)}.
   * 
   * @return the panel's current content widget
   */
  public Widget getContent() {
    return contentWrapper.getWidget();
  }

  /**
   * Gets the widget that is currently being used as a header.
   * 
   * @return the widget currently being used as a header
   */
  public Widget getHeader() {
    return header.getWidget();
  }


  /**
   * Gets a {@link HasText} instance to provide access to the headers's text, if
   * the header widget does provide such access.
   * 
   * @return a reference to the header widget if it implements {@link HasText},
   *         <code>null</code> otherwise
   */
  public HasText getHeaderTextAccessor() {
    Widget widget = header.getWidget();
    return (widget instanceof HasText) ? (HasText) widget : null;
  }

  public boolean isAnimationEnabled() {
    return isAnimationEnabled;
  }

  /**
   * Determines whether the panel is open.
   * 
   * @return <code>true</code> if panel is in open state
   */
  public boolean isOpen() {
    return isOpen;
  }

 

  public boolean remove(Widget w) {
    if (w == getContent()) {
      setContent(null);
      return true;
    }
    return false;
  }


  public void setAnimationEnabled(boolean enable) {
    isAnimationEnabled = enable;
  }

  /**
   * Sets the content widget which can be opened and closed by this panel. If
   * there is a preexisting content widget, it will be detached.
   * 
   * @param content the widget to be used as the content panel
   */
  public void setContent(Widget content) {
    final Widget currentContent = getContent();

    // Remove existing content widget.
    if (currentContent != null) {
      contentWrapper.setWidget(null);
      currentContent.removeStyleName(STYLENAME_CONTENT);
    }

    // Add new content widget if != null.
    if (content != null) {
      contentWrapper.setWidget(content);
      content.addStyleName(STYLENAME_CONTENT);
      setContentDisplay(false);
    }
  }

  private String CLOSED_ICON=" &#9658; ";
  private String OPEN_ICON=" &#9660; ";
  private GenericContainerTag strong=new GenericContainerTag("strong");
  /**
   * Sets the widget used as the header for the panel.
   * 
   * @param headerWidget the widget to be used as the header
   */
  public void setHeader(Widget headerWidget) {
	  GenericContainerTag div = new GenericContainerTag("div");
	  div.add(strong);
	  updateIcon(isOpen);
	  div.add(headerWidget);
	  header.setWidget(div);
  }
  
  public void updateIcon(Boolean open)
  {
	  if(open)
		  strong.getElement().setInnerHTML(OPEN_ICON);
	  else
		  strong.getElement().setInnerHTML(CLOSED_ICON);
  }

  /**
   * Changes the visible state of this <code>DisclosurePanel</code>.
   * 
   * @param isOpen <code>true</code> to open the panel, <code>false</code> to
   *          close
   */
  public void setOpen(boolean isOpen) {
    if (this.isOpen != isOpen) {
      this.isOpen = isOpen;
      updateIcon(isOpen);
      setContentDisplay(true);
      fireEvent();
    }
  }

  /**
   * <b>Affected Elements:</b>
   * <ul>
   * <li>-header = the clickable header.</li>
   * </ul>
   * 
   * @see UIObject#onEnsureDebugId(String)
   */
  @Override
  protected void onEnsureDebugId(String baseID) {
    super.onEnsureDebugId(baseID);
    header.ensureDebugId(baseID + "-header");
  }

  private void fireEvent() {
    if (isOpen) {
      OpenEvent.fire(this, this);
    } else {
      CloseEvent.fire(this, this);
    }
  }

  private void setContentDisplay(boolean animate) {
    if (isOpen) {
      removeStyleDependentName(STYLENAME_SUFFIX_CLOSED);
      addStyleDependentName("open");
    } else {
      removeStyleDependentName("open");
      addStyleDependentName(STYLENAME_SUFFIX_CLOSED);
    }

    if (getContent() != null) {
      if (contentAnimation == null) {
        contentAnimation = new ContentAnimation();
      }
      contentAnimation.setOpen(this, animate && isAnimationEnabled);
    }
  }

}
